﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace ITSP_Instance_Generator
{
    class Generator : IGenerator
    {
        private static readonly Random Random = new Random(); //todo seed?
        public int TotalNrNodes { get; private set; }
        public int MaxX { get; private set; }
        public int MaxY { get; private set; }
        public int MaxVisits { get; private set; }
        public int MinVisits { get; private set; }
        public int Bound { get; private set; }
        public int BoundDelta { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="totalNrNodes">The number of unique nodes</param>
        /// <param name="maxX">Maximum X coordinate</param>
        /// <param name="maxY">Maximum Y coordinate</param>
        /// <param name="minVisits">The minimum number of visits for a node. This does not count for the depot node which has to be visited 2 times. The last generated node can have less visits that MinVisits.</param>
        /// <param name="maxVisits">The maximum number of visits for a node.</param>
        /// <param name="bound">The bound</param>
        /// <param name="boundDelta">The maximum offset on the given bound</param>
        public Generator(int totalNrNodes, int maxX, int maxY, int minVisits, int maxVisits, int bound, int boundDelta)
        {
            TotalNrNodes = totalNrNodes;
            MaxX = maxX;
            MaxY = maxY;
            MinVisits = minVisits >= 1 ? minVisits : 1;
            MaxVisits = maxVisits >= MinVisits ? maxVisits : MinVisits;
            Bound = bound;
            BoundDelta = boundDelta;
        }
        public Instance Generate()
        {
            List<Node> nodes = new List<Node>();
            int nodeLabel = 1;
            int totalNrNodesAdded = 0;
            while (totalNrNodesAdded < TotalNrNodes)
            {
                Position nodePosition = new Position(Random.Next(0, MaxX+1), Random.Next(0, MaxY+1));
                int nodeNumberOfVisits;
                if (nodeLabel == 1)
                    nodeNumberOfVisits = 2;
                else
                {
                    int nrNodesToBeAdded = TotalNrNodes - totalNrNodesAdded;
                    if (MinVisits < nrNodesToBeAdded)
                        nodeNumberOfVisits = Random.Next(MinVisits, Math.Min(nrNodesToBeAdded, MaxVisits)+1);
                    else
                        nodeNumberOfVisits = nrNodesToBeAdded;
                }

                nodes.Add(new Node(nodeLabel, nodeNumberOfVisits, nodePosition));
                nodeLabel++;
                totalNrNodesAdded += nodeNumberOfVisits;
            }
            int bound = Random.Next(Math.Max(Bound - BoundDelta,0), Bound + BoundDelta + 1);
            
            return new Instance(nodes.ToArray(), bound, GenerateDistanceMatrix(nodes));
        }

        int IGenerator.GetTotalNrNodes()
        {
            return TotalNrNodes;
        }

        public static double[][] GenerateDistanceMatrix(List<Node> nodeList) //todo kan efficienter indien nodig
        {


            List<Node> expandedNodeList = new List<Node>();

            foreach (Node n in nodeList)
                for(int i = 0; i < n.NumberOfVisits; i++)
                    expandedNodeList.Add(n);

            double[][] distanceMatrix = new double[expandedNodeList.Count][];
            for (int i = 0; i < expandedNodeList.Count; i++) // initialize distanceMatrix
                distanceMatrix[i] = new double[expandedNodeList.Count];

            for (int i = 0; i < expandedNodeList.Count; i++)
                for (int j = 0; j < expandedNodeList.Count; j++)
                    distanceMatrix[i][j] =
                        expandedNodeList.ElementAt(i).Position.DistanceTo(expandedNodeList.ElementAt(j).Position);

            return distanceMatrix;
        }

        public override string ToString()
        {
            return String.Format("{0};{1};{2};{3};{4};{5};{6}", TotalNrNodes, MaxX, MaxY, MinVisits, MaxVisits, Bound, BoundDelta);
        }


    }
}
